package com.amt.olcacon.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.amt.olcacon.bo.FileData;
import com.amt.olcacon.bo.GetProcessTreeData;
import com.amt.olcacon.bo.GetProviderInfo;
import com.amt.olcacon.db.DerbyConfig;
import com.amt.olcacon.entity.ExchangeEntity;
import com.amt.olcacon.entity.ProcessEntity;
import com.amt.olcacon.entity.ResultEntity;
import com.amt.olcacon.entity.TblProcessEntity;
import com.amt.olcacon.mapper.*;
import com.amt.olcacon.service.IProductProcessService;
import com.amt.olcacon.service.ImportAndCalcResultsService;
import com.amt.olcacon.utils.BeanCopyUtils;
import com.amt.olcacon.utils.ExcelParserTwoTables;
import com.amt.olcacon.utils.ExcelParserTwoTablesAllSheet;
import com.amt.olcacon.utils.OlcaUtils;
import com.amt.olcacon.vo.request.ExchangeRequest;
import com.amt.olcacon.vo.request.FlowRequest;
import com.amt.olcacon.vo.request.ProcessRequest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;

import jakarta.persistence.criteria.CriteriaBuilder;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.jdbc.Null;
import org.openlca.core.DataDir;
import org.openlca.core.database.Derby;
import org.openlca.core.database.FlowPropertyDao;
import org.openlca.core.database.ImpactMethodDao;
import org.openlca.core.database.ProcessDao;
import org.openlca.core.math.SystemCalculator;
import org.openlca.core.model.CalculationSetup;
import org.openlca.core.model.Process;
import org.openlca.core.results.LcaResult;
import org.openlca.core.results.SystemProcess;
import org.openlca.util.Processes;
import org.openlca.util.Results;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestParam;

import java.io.IOException;
import java.util.*;

@Service
@Log4j2
public class ImportAndCalcResultsServiceImpl implements ImportAndCalcResultsService {

    @Autowired
    private ProductProcessMapper productProcessMapper;

    @Autowired
    private IProductProcessService productProcessService;

    @Autowired
    private ProcessMapper processMapper;

    @Autowired
    private ExchangeMapper exchangeMapper;

    @Autowired
    private ResultMapper resultMapper;

    @Autowired
    private ExcelParserTwoTablesAllSheet excelParserTwoTablesAllSheet;

    @Autowired
    private TblProcessesMapper tblProcessesMapper;



    public void addTree (String filePath)  {
        try{
            List<GetProcessTreeData> getProcessTreeData =  getProcessTree(filePath);
            log.info("获取 processTreeDataList 为:{}", JSONUtil.toJsonStr(getProcessTreeData));
            List<GetProcessTreeData> errorProcessTreeData = new ArrayList<>();

            for (GetProcessTreeData getProcessTreeDatum : getProcessTreeData) {

                //打印当前执行的数据是第几个
                log.info("正在处理第{}个processTreeData", getProcessTreeData.indexOf(getProcessTreeDatum) + 1);
                log.info("===============================================================================================");
                ProcessRequest processRequest= getProcessTreeDatum.getProcessRequest();
                String name = processRequest.getName();
                Long processId = processRequest.getId();
                Long impactMethodId = 64262539L;
                productProcessService.addTree(getProcessTreeDatum);
                Long result= addByProcess(true, true, name, processId, impactMethodId, "");
                if(result==0L){
                    errorProcessTreeData.add(getProcessTreeDatum);
                }
            }

            var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);

            log.info("未添加完成的数据是：errorProcessTreeDataList:{}", JSONUtil.toJsonStr(errorProcessTreeData));



        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }



    public Long addByProcess(Boolean createProcess,Boolean withMetaData,String name,Long processId,Long impactMethodId,String description) {

        Map<String,String> errorData = new HashMap<>();
        try (var db = DataDir.get().openDatabase(DerbyConfig.DBNAME)) {
            org.openlca.core.model.Process process = new ProcessDao(db).getForId(processId);

            if (process == null) {
                log.info("流程不存在！");
                return null;
            }

            var system = db.get(Process.class, processId);
            // 获取processRefId对应的Process对象
            var method = new ImpactMethodDao(db).getForId(impactMethodId);
            // 获取impactMethodRefId对应的ImpactMethod对象
            var setup = CalculationSetup.of(system)
                    .withImpactMethod(method);
            // 创建CalculationSetup对象
            var calc = new SystemCalculator(db);
            // 创建SystemCalculator对象
            LcaResult lcaResult = calc.calculate(setup);

            Gson gson = new Gson();
            if (createProcess) {
                var sysProc = withMetaData ?
                        SystemProcess.createWithMetaData(db, setup, lcaResult, name) :
                        SystemProcess.create(db, setup, lcaResult, name);
                db.insert(sysProc);
                log.info("createProcess success! ProcessId is :{}", sysProc.id);
                //保存Process
                ProcessEntity pe = OlcaUtils.buildProcessEntity(sysProc);
                pe.setDescription( description);
                int count = processMapper.insert(pe);
                log.info("insert ProcessEntity success! count:{} pe:{}", count, gson.toJson(pe));

                // 保存 exchanges
//                sysProc.exchanges.forEach(exchange -> {
//                    ExchangeEntity ee = OlcaUtils.buildExchangeEntity(exchange, sysProc);
//                    int c = exchangeMapper.insert(ee);
//                    //log.info("insert ExchangeEntity success! count:{} ee:{}", c, gson.toJson(ee));
//                });
//                log.info("insert ExchangeEntity success! count:{}", sysProc.exchanges.size());
                return sysProc.id;
            } else {
                var result = Results.createFrom(db, setup, lcaResult);
                result.name = name;
                db.insert(result);
                log.info("createResults success! ResultsId is :{}", result.id);

                //保存Results
                ResultEntity re = BeanCopyUtils.copy(result, ResultEntity.class);
                re.setDescription(description);
                if (null != re.getName()) {
                    int count = resultMapper.insert(re);
                    log.info("insert ResultEntity success! count:{} re:{}", count, gson.toJson(re));
                }
                return result.id;
            }

        } catch (Exception e) {
            log.error("计算失败！", e);

            return 0L;
        }
    }


    public List<GetProcessTreeData> getProcessTree(String filePath) throws IOException {

        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
        List<GetProcessTreeData> processTreeDataList = new ArrayList<>();
        List<FileData> fileDataList = excelParserTwoTablesAllSheet.getFileData(filePath);

        fileDataList.forEach(fileData -> {
            Map<String,String> processMap = fileData.getProduct();
            List<Map<String,String>> exchangeList =fileData.getExchange();

            // 首先获得 flow 的信息
            List<FlowRequest> flowRequestList = new ArrayList<>();

            exchangeList.forEach(flow -> {flow.put("id",String.valueOf(getSnowflakeId()));
                flow.put("refId", UUID.randomUUID().toString());
                FlowRequest flowRequest = new FlowRequest();
                flowRequest.setId(Long.valueOf(flow.get("id")));
                flowRequest.setRefId(flow.get("refId"));
                flowRequest.setName(flow.get("name"));
                var flowProperties = new FlowPropertyDao(db).getForName(flow.get("unit_group"));
                if (flowProperties != null && !flowProperties.isEmpty()) {
                    var flowproperty = flowProperties.getFirst();
                    flowRequest.setFlowPropertyId(flowproperty.id);
                    Map<Long ,Double> propertyFators = new HashMap<>();
                    propertyFators.put(flowproperty.id,1.0);
                    flowRequest.setPropertyFators(propertyFators);
                    flowRequestList.add(flowRequest);
                } else {
                    log.warn("未找到名称为 '{}' 的流属性", flow.get("unit_group"));
                }

            });


            // 获得process 的信息
            ProcessRequest processRequest = getProcessRequest(db, processMap, exchangeList);

            GetProcessTreeData getProcessTreeData = new GetProcessTreeData();
            getProcessTreeData.setProcessRequest(processRequest);
            getProcessTreeData.setFlowRequestList(flowRequestList);
            processTreeDataList.add(getProcessTreeData);
        });

        return processTreeDataList;


    }



    public ProcessRequest getProcessRequest(Derby db, Map<String,String> processMap, List<Map<String,String>> exchangeList){

        ProcessRequest processRequestList = new ProcessRequest();
        processRequestList.setId(getSnowflakeId());
        processRequestList.setRefId(UUID.randomUUID().toString());
        processRequestList.setName(processMap.get("process_name"));

        Map<String,String> productMainFlow = exchangeList.stream().filter(exchange -> exchange.get("type").equals("主产品")).findFirst().orElse(null);
        processRequestList.setFlowId(Long.valueOf(productMainFlow.get("id")));
        processRequestList.setFlowRefId(productMainFlow.get("refId"));

        List<ExchangeRequest> inputIds = new ArrayList<>();
        List<ExchangeRequest> outputIds = new ArrayList<>();

        for (Map<String, String> exchange : exchangeList) {

            String ioType = exchange.get("io_type");
            ExchangeRequest exchangeRequest = new ExchangeRequest();
            Long id = Long.valueOf(exchange.get("id"));
            String refId = exchange.get("refId");
            Double amount = Double.valueOf(exchange.get("enter_value"));
            Long providerId = null;
            String provicer = exchange.get("background_name");
            String providerLocation = exchange.get("country_code");
            if(StringUtils.isNotBlank(provicer)){
                List<GetProviderInfo> providerInfo = productProcessMapper.getProviderInfo(provicer, providerLocation);
                if(!providerInfo.isEmpty()){
                    providerId = providerInfo.getFirst().getId();
                }
            }

            var flowProperties = new FlowPropertyDao(db).getForName(exchange.get("unit_group"));
            if (flowProperties == null || flowProperties.isEmpty()) {
                log.warn("未找到名称为 '{}' 的流属性", exchange.get("unit_group"));
                continue;
            }
            var flowProperty = flowProperties.getFirst();
            Long unitId = flowProperty.getReferenceUnit().id;
            Long propertyId = flowProperty.id;
            exchangeRequest.setFlowId(processRequestList.getFlowId());
            exchangeRequest.setFlowRefId(processRequestList.getFlowRefId());
            exchangeRequest.setUnitId(unitId);
            exchangeRequest.setPropertyId(propertyId);
            exchangeRequest.setAmount(amount);
            exchangeRequest.setDefaultProviderId(providerId);

            if("产品".equals(ioType)||"输出".equals(ioType)){
                exchangeRequest.setFlowId(processRequestList.getFlowId());
                exchangeRequest.setFlowRefId(processRequestList.getFlowRefId());
                exchangeRequest.setUnitId(unitId);
                exchangeRequest.setPropertyId(propertyId);
                exchangeRequest.setAmount(amount);
                exchangeRequest.setDefaultProviderId(providerId);
                outputIds.add(exchangeRequest);
            }

            if("输入".equals(ioType)){
                exchangeRequest.setFlowId(id);
                exchangeRequest.setFlowRefId(refId);
                exchangeRequest.setUnitId(unitId);
                exchangeRequest.setPropertyId(propertyId);
                exchangeRequest.setAmount(amount);
                exchangeRequest.setDefaultProviderId(providerId);
                inputIds.add(exchangeRequest);
            }
        }
        processRequestList.setInputIds(inputIds);
        processRequestList.setOutputIds(outputIds);


        return processRequestList;

    }

    /**
     * 使用雪花算法生成16为的id
     */
    public Long getSnowflakeId() {
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        long id = snowflake.nextId();
        // 固定16位
        return id % 10000000000000000L;
    }


    @Override
    public void clearCalcResults() {
    var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
    var processdao = new ProcessDao(db);
    Map<String, Long> parameters = new HashMap<>();
    parameters.put("categoryId", 63763210L);
    // 修改查询语句，添加WHERE条件和命名参数
    List<Process> processes = processdao.getAll("select o from Process o where o.id > :categoryId", parameters);
    processdao.deleteAll(processes);
    QueryWrapper<TblProcessEntity> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().gt(TblProcessEntity::getId,63763210);
    tblProcessesMapper.delete(queryWrapper);
}

//    public void clearCalcResults() {

//        var db = DataDir.get().openDatabase(DerbyConfig.DBNAME);
//        var processdao = new ProcessDao(db);
//        Map<String, Long> parameters = new HashMap<>();
//        parameters.put("o.F_CATEGORY",10086L);
//        List<Process> processes = processdao.getAll("select o from Process o",parameters);
//        log.info("查询到的process列表：{}",JSONUtil.toJsonStr(processes));
////        List<Long> ids = tblProcessesMapper.selectByCategory();
////        for (Long id : ids) {
////
////            if(null != processdao.getForId(id)){
////                log.info("删除id为{}的流程",id);
////                processdao.delete(id);
////            }
////        }
////        QueryWrapper<TblProcessEntity> queryWrapper = new QueryWrapper<>();
////        queryWrapper.lambda().eq(TblProcessEntity::getFCategory,10086);
////        tblProcessesMapper.delete(queryWrapper);
//    }


}
